package org.crown.project.api.wechat;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.Arrays;
import org.junit.Test;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.validation.constraints.NotEmpty;
import java.security.*;

/**
 * 微信小程序数据解密算法
 * @author WuFengSheng on 2019/06/19.
 */
@Slf4j
public class WxAesUtil {

    /**
     * 算法名称
     */
    private final static String KEY_ALGORITHM = "AES";

    /**
     * 加解密算法/模式/填充方式
     */
    private final static String ALGORITHM = "AES/CBC/PKCS7Padding";

    /**
     * 初始会话密钥
     * @param sessionKeyByte
     */
    private static Key init(byte[] sessionKeyByte) {
        /**如果密钥不足16位，那么就补足. 这个if 中的内容很重要*/
        int base = 16;
        if (sessionKeyByte.length % base != 0) {
            int groups = sessionKeyByte.length / base + (sessionKeyByte.length % base != 0 ? 1 : 0);
            byte[] temp = new byte[groups * base];
            Arrays.fill(temp, (byte) 0);
            System.arraycopy(sessionKeyByte, 0, temp, 0, sessionKeyByte.length);
            sessionKeyByte = temp;
        }
        /**将提供程序添加到下一个可用位置*/
        Security.addProvider(new BouncyCastleProvider());
        /**转化成JAVA的密钥格式*/
        return new SecretKeySpec(sessionKeyByte, KEY_ALGORITHM);
    }

    /**
     * 解密方法
     * @param encryptedData 包括敏感数据在内的完整用户信息的加密数据
     * @param sessionKey    会话密钥
     * @param iv            加密算法的初始向量
     * @return  返回字节数组
     */
    public static byte[] decrypt(@NotEmpty String encryptedData, @NotEmpty String sessionKey, @NotEmpty String iv) {
        try{
            byte[] encryptedDataByte   = Base64.decodeBase64(encryptedData);
            byte[] sessionKeyByte      = Base64.decodeBase64(sessionKey);
            byte[] ivByte              = Base64.decodeBase64(iv);
            Key key = init(sessionKeyByte);
            /**获取cipher实例*/
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            /**初始化cipher*/
            cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ivByte));
            return cipher.doFinal(encryptedDataByte);
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        }catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }  catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }catch (NoSuchPaddingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 解密方法
     * @param encryptedData 包括敏感数据在内的完整用户信息的加密数据
     * @param sessionKey    会话密钥
     * @param iv            加密算法的初始向量
     * @return  返回字符串
     */
    public synchronized static String decrypt(@NotEmpty String encryptedData, @NotEmpty String sessionKey, @NotEmpty String iv, @NotEmpty String charSet){
        try{
            byte[] decrypted = decrypt(encryptedData,sessionKey,iv);
            int pad = decrypted[decrypted.length - 1];
            if (pad < 1 || pad > 32) {
                pad = 0;
            }
            return new String(java.util.Arrays.copyOfRange(decrypted, 0, decrypted.length - pad),charSet);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 测试方法
     */
    @Test
    public void test(){
        String encryptedData = "Fm0Tdf8lvLUCGLJkepdSMm+5mCx7z7rK17qjZK+K+S7+sK0Zt8sVZhJXhCkyMq/9f0L2FIfRczQXI7FVrG/RDIokpLSkNPeoheKaxEfbHeY/5CSsTUEQWOtL40IepzPOTgerUdLBW1G0i2fjvfswPdtmeegYNoz31nVsHYzbDTspTXyiPQHmnYUYk8OBTkjYe2hiN2emhkj6/yjcIo61OA==";
        String sessionKey = "6Edpd0bMeApgx9qr7ScQ7w==";
        String iv = "F1rjR+8gbPyJ63TLhrHTLQ==";

//        byte[] resultByte = WxAesUtil.decrypt(encryptedData, sessionKey, iv);
//        if(null != resultByte && resultByte.length > 0){
//            String userInfo = new String(WxPKCS7Encoder.decode(resultByte));
//            log.info(userInfo);
//        }

        log.info(WxAesUtil.decrypt(encryptedData,sessionKey,iv,"utf-8"));
        /**
         * {"phoneNumber":"18558732683","purePhoneNumber":"18558732683","countryCode":"86","watermark":{"timestamp":1534828484,"appid":"wxa104fc81a1774c6d"}}
         */
    }
}
